<?php

/*****************************************************

  Copyright:	(c) 2006  SeaDesign Web Studio
        Web:	www.seadesign.ru
      Email: 	seadesign@gmail.com
  
*****************************************************/

if (!function_exists('array_set')) {
	function array_set(&$array, &$val) {
		if (!in_array($val, $array)) {
			return ($array[] = $val);
		}
		return null;
	}
}

require_once('db.inc');

define('FIELD_TYPE_STRING', 	0);
define('FIELD_TYPE_TEXT', 		1);
define('FIELD_TYPE_INTEGER', 	2);
define('FIELD_TYPE_DATETIME', 3);
define('FIELD_TYPE_DATE',     4);
define('FIELD_TYPE_FLOAT',    5);
define('FIELD_TYPE_FILE', 		7);
define('FIELD_TYPE_FAKE', 		8);
define('FIELD_TYPE_DICT', 		9);


/*************************************************************************
*
* Information about dictionary assigned to a table field
*
*************************************************************************/
class Dictionary 
{
	function Dictionary($catname, $fname) 
	{
		global $db;
		$q = new Select($db, 'cf_dictionaries');
		$q->addFields('dict_name','dict_key','dict_val','add_where');
		$q->where('cat_name',$catname);
		$q->where('field_name',$fname);
		$q->run();
		if ($q->fetch()) {
			$this->dictName = $q->at(0);
			$this->dictKey = $q->at(1);
			$this->dictVal = $q->at(2);
			$this->addWhere = $q->at(3);
		}
		$q->close();
	}
	
	function exist() {
		return $this->dictName ? true : false;
	}
	
	function getName() {
		return $this->dictName;
	}
	function getKeyFieldName() {
		return $this->dictKey;
	}
	function getValueFieldName() {
		return $this->dictVal;
	}
	function getConditions() {
		return $this->addWhere;
	}
	
//private
	var
		$dictName,
		$dictKey,
		$dictVal,
		$addWhere;
};



function resolveDict(&$qSelect, $catnm, $fnm, $nullAllow) 
{
	$dict = new Dictionary($catnm, $fnm);
	if ($dict->exist()) {
					//add dictionary for field
		if ($nullAllow) {
			$qSelect->leftJoin($dict->getName(), $fnm, $dict->getKeyFieldName());
		}
		else {
			$qSelect->innerJoin($dict->getName(), $fnm, $dict->getKeyFieldName());
		}
		$qSelect->addWhereString( $dict->getConditions() );
		return $qSelect->addFields($dict->getValueFieldName());
	}

		//add field itself
	return $qSelect->addFields($fnm);
	

	// returns count of fields in $qSelect
}




/*************************************************************************
*
* Files
*
*************************************************************************/

class Files 
{
	function __construct($ownerName) 
	{
		$this->ownerName = $ownerName;
		$this->tname = $this->getTableName();
		$this->src = null;
		$this->idFieldName = null;
	}
	
	public function open(&$allFields, $fnames)
	{
		$ownerId = is_array($allFields) ? $this->getOwnerIdFromFields($allFields) : $allFields;
		$this->_open($ownerId, $fnames);
	}

	public function openAll($fname)
	{
		$this->_open(null, $fname);
	}
	
	public function fetch() 
	{
		if ($this->src) {
			return $this->src->fetch() ? true : false;
		}
		return false;
	}
	
	public function current() 
	{
		$fname = $this->src->at(0);
		$fileInfo = $this->getFileInfo($fname);
		return
			$this->fpath(
				$fileInfo['path'],
				$this->ownerName, 
				$this->src->at(1), 
				$fname,
				$this->src->at(2)
			);
	}
	
	public function getFileInfo($fnm)
	{
		global $db;
		$q = new Select($db, 'cf_fields');
		$q->addFields('fmt','length');
		$q->where('cat_name', $this->ownerName);
		$q->where('name', $fnm);
		$q->run();
		
		$fileinfo = array();
		if ($q->fetch()) {
			$params = explode(';',$q->at(0));
			$fileinfo['path'] = $params[0];
			$fileinfo['types'] = explode(',',$params[1]);
			$fileinfo['length'] = $q->at(1);
		}
		else {
			die("There is no information in 'sys\$fields' for uploading field " . $this->ownerName . "." . $fnm);
		}
		return $fileinfo;
	}
	
	
	public function upload($allFields)
	{
		if (!count($_FILES)) 
		{
			return;
		}
		
		$ownerId = $this->getOwnerId($allFields);
		
		foreach ($_FILES as $fnm => $params) 
		{
			if (!$this->uploadErrorCheck($params['error'], $params['tmp_name'])) 
			{
				continue;
			}
			
			$fileInfo = $this->getFileInfo($fnm);
			if ($fileInfo['length'] && ($params['size'] > $fileInfo['length'])) 
			{
				die("File upload error: size of file ". $fnm . " exceeds maximum size set for this field");
			}
			
			$filetype = substr(strrchr($params['name'],'.'),1);
			
			if (!in_array(strtolower($filetype), $fileInfo['types']) && !in_array(strtoupper($filetype), $fileInfo['types'])) 
			{
				die("File upload error: invalid file type");
			}
			
			$this->delExistedFile($ownerId, $fnm, $fileInfo['path']);
			
			global $db;
			$q = new Insert($db,'cf_files');
			$q->addFields(
				array(
					'owner_name' => $this->ownerName,
					'owner_id' => $ownerId,
					'name' => $fnm,
					'type' => $filetype
				)
			);
			$q->run();
			
			$fpath = $this->fpath($fileInfo['path'], $this->ownerName, $ownerId, $fnm, $filetype);
			$uploadAs = '../../' . $fpath;
			
			if (!move_uploaded_file($params['tmp_name'], $uploadAs)) 
			{
				$this->delExistedFile($ownerId, $fnm, $fileInfo['path']);
				die('File upload error: can not move uploaded file');
			}
			chmod($uploadAs, 0644);
			
			$this->updateOwnerTable($ownerId, $fnm, $fpath);
		}
	}


	public function del($fnm, &$allFields, $path)
	{
		$fileInfo = $this->getFileInfo($fnm);
		$this->delExistedFile(
			$this->getOwnerId($allFields), 
			$fnm, 
			$path . $fileInfo['path']
		);
	}



	private function updateOwnerTable($ownerId, $fnm, $fpath)
	{
		try {
			$tname = query2var("SELECT tname FROM cf_categories WHERE name='{$this->ownerName}'");
			execQuery(
				"UPDATE $tname SET $fnm=:fpath WHERE {$this->idFieldName}=:id",
				$fpath,
				$ownerId
			);
		}
		catch (Exception $e)
		{
			//there is no field to store file URL in owner table, so just didn't store it
		}
	}
	
	private function getTableName()
	{
		global $db;
		$q = new Select($db, 'cf_categories');
		$q->addFields('tname');
		$q->where('name', $this->ownerName);
		$q->run();
		if (!$q->fetch()) {
			die ("Record with category name [$this->ownerName] not found\nFiles");
		}
		return $q->at(0);
	}

	private function _open($ownerId, $fnames)
	{
		global $db;
		$this->src = new Select($db, 'cf_files');
		$this->src->addFields('name', 'owner_id', 'type');
		
		$this->src->where('owner_name', $this->ownerName);
		if (!is_null($ownerId)) {
			$this->src->where('owner_id', $ownerId);
		}
		$this->src->where('name', $fnames);

		$this->src->order('name',ASC);
		$this->src->run();
	}
	
	private function fpath($path, $ownerName, $ownerId, $name, $filetype) 
	{
		return $path . '/' . $ownerName . $ownerId . $name . '.' . $filetype;
	}

	private function delExistedFile($ownerId, $fnm, $path) 
	{
		global $db;
		
		$q = new Select($db, 'cf_files');
		$q->addFields('type');
		$q->where('owner_name', $this->ownerName);
		$q->where('owner_id', $ownerId);
		$q->where('name', $fnm);
		$q->run();
		
		$ftype = null;
		if ($q->fetch()) 
		{
			$ftype = $q->at(0);
		}
		$q->close();
		
		if (!is_null($ftype))	//record exists
		{
			$q = new Delete($db,'cf_files');
			$q->where('owner_name', $this->ownerName);
			$q->where('owner_id', $ownerId);
			$q->where('name', $fnm);
			$q->where('type', $ftype);
			$q->run();
			
			@unlink( $this->fpath($path,$this->ownerName,$ownerId,$fnm,$ftype)	);
		
			$this->updateOwnerTable($ownerId, $fnm, null);
		}
	}
	
	private function getOwnerId(&$allFields)
	{
		$id = $this->getOwnerIdFromFields($allFields);
		if (!$id) {
			global $db;
			$q = new Select($db, $this->tname);
			$q->addFields("MAX($this->idFieldName)");
			$q->run();
			$q->fetch();
			return $q->at(0);
		}
		return $id;
	}
	
	private function getOwnerIdFromFields(&$allFields)
	{
		global $db;
		$q = new Select($db, 'cf_file_owners');
		$q->addFields('idfield');
		$q->where('owner_name', $this->ownerName);
		$q->run();
		if ($q->fetch()) {
			$this->idFieldName = $q->at(0);
			if (isset($allFields[$this->idFieldName])) {
				return $allFields[$this->idFieldName];
			}
			return null;			
		}
		die("There is no record for " . $this->ownerName . " in file_owners table");		
	}
	
	private function uploadErrorCheck($err, $tmpName)
	{
		if ($err != UPLOAD_ERR_OK) {
			if ($err == UPLOAD_ERR_NO_FILE) {
				return false;
			}
			else if ($err == UPLOAD_ERR_INI_SIZE) {
				die("File upload error: file size exceeds 'upload_max_filesize' parameter set in php.ini");
			}
			die("File upload error: error code [$err]");
		}
		
		if (!is_uploaded_file($tmpName)) {
			die('File upload error: file was not actually uploaded');
		}
		
		//if nothing happens check also 'post_max_size'
		
		return true;
	}
	

	private $ownerName;
	private $tname;
	private $src;
	private $idFieldName;
};




class ArrayOfItems
	implements IteratorAggregate
{
	public function getIterator() 
	{
		return new ArrayIterator($this->items);
	}

	public function toArray()
	{
		return $this->items;
	}
	
	public function is_empty()
	{
		return empty($this->items);
	}
	
	public function append(ArrayOfItems $items)
	{
		foreach ($items as $item) {
			$this->add($item);
		}
	}
	
	public function __toString()
	{
		$s = '';
		foreach ($this->items as $item) {
			$s .= $item->__toString() . "<br>\n";
		}
		return $s;
   }


	
	protected function add($item) 
	{
		$this->items[] = $item;
	}
	
	protected function limitAsSQL()
	{
		
	}
	
	protected $items = array();
};


?>